#pragma once 

#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
#include <mutex>
#include <memory>
#include <unordered_map>
#include "logger.hpp"

typedef websocketpp::server<websocketpp::config::asio> wsserver_t ;
typedef enum{UNLOGIN,LOGIN} ss_statu;

class session{
private:
    uint64_t _ssid;//标识符
    uint64_t _uid;//session对应的用户id
    ss_statu _statu;//用户状态:未登录，已登录
    wsserver_t::timer_ptr _tp;//session对应关联对应的定时器
public:
    session(uint64_t ssid):_ssid(ssid){ DBG_LOG("SESSION %p 被创建",this);}
    ~session(){DBG_LOG("SESSION %p 被销毁",this);}
    void set_statu(ss_statu statu){_statu=statu;}
    void set_user(uint64_t uid){_uid=uid;}
    void set_timer(const wsserver_t::timer_ptr &tp){_tp=tp;}
    uint64_t get_user(){return _uid;}
    uint64_t get_ssid(){return _ssid;}
    bool is_login(){return _statu==LOGIN;}
    wsserver_t::timer_ptr& get_timer(){return _tp;}
};

#define SESSION_TIMEOUT 35000
#define SESSION_FOREVER -1
using session_ptr=std::shared_ptr<session>;
class session_manager{
private:
    uint64_t _next_ssid;
    std::mutex _mutex;
    std::unordered_map<uint64_t,session_ptr> _sessions;
    wsserver_t *_server;
public:
    session_manager(wsserver_t* server):_server(server),_next_ssid(1){
        DBG_LOG("session管理器初始化完毕!");
    }
    ~session_manager(){
        DBG_LOG("session管理器即将销毁!");
    }
    session_ptr create_session(uint64_t uid,ss_statu statu){
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr ssp(new session(_next_ssid));
        ssp->set_statu(statu);
        ssp->set_user(uid);
        _sessions.insert(std::make_pair(_next_ssid,ssp));
        _next_ssid++;
        return ssp;
    }
    session_ptr get_session_by_ssid(uint64_t ssid){
        std::unique_lock<std::mutex> lock(_mutex);
        auto it=_sessions.find(ssid);
        if(it==_sessions.end()){
            return session_ptr();
        }
        return it->second;
    }
    void remove_session(uint64_t ssid){
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.erase(ssid);
    }
    void append_session(const session_ptr& ssp){
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.insert(std::make_pair(ssp->get_ssid(),ssp));
    }
    void set_session_expire_time(uint64_t ssid,int ms){
        //依赖于websocketpp的定时器来完成session⽣命周期的管理。
        // 登录之后，创建session，session需要在指定时间⽆通信后删除
        // 但是进⼊游戏⼤厅，或者游戏房间，这个session就应该永久存在
        // 等到退出游戏⼤厅，或者游戏房间，这个session应该被重新设置为临时，在⻓时间⽆通信后被删除
        session_ptr ssp=get_session_by_ssid(ssid);
        if(ssp.get()==nullptr){
            return;
        }
        wsserver_t::timer_ptr tp=ssp->get_timer();
        if(tp.get()==nullptr&& ms==SESSION_FOREVER){
            //1. 在session永久的情况下，设置永久存在
            return;
        }else if(tp.get()==nullptr && ms!=SESSION_FOREVER){
            //2. 在session永久的情况下，设置指定时间之后删除
            wsserver_t::timer_ptr tmp=_server->set_timer(ms,std::bind(&session_manager::remove_session,this,ssid));
            ssp->set_timer(tmp);
        }else if(tp.get()!=nullptr && ms==SESSION_FOREVER){
            //3. 在session设置了删除任务的情况下，设置永久存在
            /*注意：因为这里的取消并不是真正的取消，这种取消的特性是会导致任务直接被执行,所以我们需要重新添加session信息*/
            tp->cancel();//因为这个取消定时任务并不是⽴即取消的,所以下面我们重新添加session关联的时候也要设置定时任务的方式添加
            ssp->set_timer(wsserver_t::timer_ptr());//设置永久存在
            _server->set_timer(0,std::bind(&session_manager::append_session,this,ssp));
        }else if(tp.get()!=nullptr && ms!=SESSION_FOREVER){
            //4. 在session设置了删除任务的情况下，将session重置删除时间
            tp->cancel();//因为这个取消定时任务并不是⽴即取消的,所以下面我们重新添加session关联的时候也要设置定时任务的方式添加
            ssp->set_timer(wsserver_t::timer_ptr());
            _server->set_timer(0,std::bind(&session_manager::append_session,this,ssp));
            wsserver_t::timer_ptr tmp=_server->set_timer(ms,std::bind(&session_manager::remove_session,this,ssid));
            ssp->set_timer(tmp);
        }
    }
};